/*
 * SmartCam master processor library
 * Combinatorial coprocessor mapping.
 */

#include <float.h>
#include <log.h>
#include "globals.h"
#include "mapping.h"
#include "evaluate.h"
#include "combinatorial.h"

#define SC_MAP_UNMAPPED_PENALIZE       1000
#define SC_MAP_UNMAPPED_CYCLE_PENALIZE 100000

extern table_t scm_procs, scm_ops, scm_streams, /* owned, indexed */
               scm_links;                       /* owned */

int scMapCombinatorial(void)
{
  int id=-1, id2=-1, resets, dof=0, fail=0;
  table_t mapping; /* scm_proc_t */
  double value, best=-DBL_MAX;
  scm_proc_t *p;
  scm_op_t *o;

  /* Initialize mapping */
  abortOnNegative(STREAM(scl.err), tblcreate(&mapping, sizeof(scm_proc_t)));

  id = -1;
  while (tblget(&scm_ops, id = tblgetnexti(&scm_ops, id), &o))
  {
    if (!o->fixed)
    {
      int id=-1;

      dof++;
      if (o->processor)
	scMapUnassignProcessor(o);

      /* Initialize mapping to first processor that can execute the operation */
      while (tblgetnextelement(&scm_procs, &id, &p))
	if (tblget(&p->target->operations, o->operation->id, NULL))
	{
	  if (!scMapAssignProcessor(o, p))
	    break;
	}
    }
  }

  if (dof)
  {
    lprintf(STREAM(scl.info), "Finding a combinatorial mapping for %d operations", dof);
  }
  else
  {
    lprintf(STREAM(scl.info), "Unable to map any operations");
    abortOnNegative(STREAM(scl.err), tbldestroy(&mapping, 0));
    return 1;
  }

  /* Try all combinations */
  do
  {
    /* Do the evaluation */
    value = scEvlMapping(1);

    /* Penalize for unmapped operations */
    while (tblget(&scm_ops, id = tblgetnexti(&scm_ops, id), &o))
      if (!o->fixed && !o->processor)
      {
	if (o->operation->id == CYCLE)
	  value = 1/(1/value + SC_MAP_UNMAPPED_CYCLE_PENALIZE);
	else
	  value = 1/(1/value + SC_MAP_UNMAPPED_PENALIZE);
      }
   
    if (value > best)
    {
      abortOnNegative(STREAM(scl.err), tblclear(&mapping, 0));
      id = -1;
      while (tblget(&scm_ops, id = tblgetnexti(&scm_ops, id), &o))
	if (o->processor)
	  abortOnNegative(STREAM(scl.err), tbladdi(&mapping, o->processor, o->id));
      best = value;
    }
    
    /* Update the mapping */
    resets = 0;
    id = -1;
    while (tblget(&scm_ops, id = tblgetnexti(&scm_ops, id), &o))
    {
      if (!o->fixed)
      {
	if (o->processor)
	{
	  id2 = o->processor->target->id;
	  scMapUnassignProcessor(o);
	}
	else
	  id2 = -1;

	while (tblgetnextelement(&scm_procs, &id2, &p))
	  if (tblget(&p->target->operations, o->operation->id, NULL))
	    /* Next victim */  
	    if (!scMapAssignProcessor(o, p))
	      break;

	if (p)
	  break;

	/* Reset to 0 */
	while (tblgetnextelement(&scm_procs, &id2, &p))
	  if (tblget(&p->target->operations, o->operation->id, NULL))
	    if (!scMapAssignProcessor(o, p))
	      break;

	resets++;
      }
    }
  } while (resets < dof);

  /* Clear mapping */
  id = -1;
  while (tblget(&scm_ops, id = tblgetnexti(&scm_ops, id), &o))
    if (!o->fixed && o->processor)
      scMapUnassignProcessor(o);

  /* Set to best mapping */
  id = -1;
  while (tblget(&scm_ops, id = tblgetnexti(&scm_ops, id), &o))
  {
    tblget(&mapping, o->id, &p);
    if (!o->fixed && p)
      abortOnError(STREAM(scl.err), scMapAssignProcessor(o, p));

    if (!o->processor)
      fail = 1;
  }

  abortOnNegative(STREAM(scl.err), tbldestroy(&mapping, 0));

  return fail;
}
